﻿using System.Collections.Generic;
using OMC.Classification;
using Outlook = Microsoft.Office.Interop.Outlook;
using System.Threading;
using Settings = OMC.Properties.Settings;
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace OMC.DataManagement
{
    /// <summary>
    /// An object of this class could manage all the things about the
    /// handling of the Add-In.
    /// </summary>
    class Manager
    {
        private List<Folder> folderList;
        private ClassificationHandler classificationHandler { get; set; }
        private bool isDataSetLoaded = false;
        private int folderIndex = 1000;
        private List<String> defaultFolderList;
        private Outlook.Stores stores;
        private Outlook.NameSpace nameSpace;

        /// <summary>
        /// The constructor to create a object.
        /// </summary>
        /// 
        /// <param name="stores">actual outlook stores</param>
        /// <param name="nameSpace">actual outlook namespace</param>
        public Manager(Outlook.Stores stores, Outlook.NameSpace nameSpace)
        {
            this.stores = stores;
            this.nameSpace = nameSpace;
            this.folderList = new List<Folder>();
            this.classificationHandler = new ClassificationHandler();
            this.init();
        }

        /// <summary>
        /// This Method load the whole dataset after loading
        /// the properties.
        /// </summary>
        public void init()
        {
            this.CreateDefaultFolderList();
            this.loadCustomProperties();
            this.Load();
            Utils.Debug.Log("Initialized Manager");
        }

        /// <summary>
        /// This method loads the mail data of all accounts into the referenced list
        /// which contain folders which contain mailcontents. The loaded maildata
        /// are "mirrored" in the list of folder items.
        /// </summary>
        /// <returns>a list of folders of outlook</returns>
        public List<Folder> LoadMailData()
        {
            List<Folder> folderList = new List<Folder>();
            foreach (Outlook.Store store in stores)
            {
                try
                {
                    Outlook.Folder RootFolder = store.GetRootFolder() as Outlook.Folder;
                    CreateMailData(RootFolder, folderList);
                }
                catch (COMException ex)
                {
                    String message = "It isn't possible to load mail data of Store";
                    Utils.Debug.Log(message, ex, TraceEventType.Error);
                }
            }
            return folderList;
        }

        public List<ResultItem> Classify(MailContent mailToClassify)
        {
            return classificationHandler.Classify(mailToClassify, folderList);
        }

        /// <summary>
        /// This is a recursiv method, to create MailContents of the MailItems of
        /// all accounts. A few default folders are ignored, such as
        /// inbox, outbox, deleted items..
        /// </summary>
        /// <param name="rootFolder">the actual rootFolder</param>
        /// <param name="folderList">the referenced list of folder items</param>
        private void CreateMailData(Outlook.Folder rootFolder, List<Folder> folderList)
        {
            try
            {
                Outlook.Folders childFolders = rootFolder.Folders;
                if (childFolders.Count > 0)
                {
                    foreach (Outlook.Folder childFolder in childFolders)
                    {
                        string folderPath = "not assigned";
                        try
                        {
                            folderPath = childFolder.FolderPath;
                            if (ChekFolder(childFolder))
                            {
                                //Check, if childFolder contains any elements
                                if (childFolder.Items.Count > 0)
                                {
                                    //inAccount.DisplayName = Account Name
                                    Folder folder = new Folder(childFolder.Name, folderIndex.ToString(), childFolder.EntryID);
                                    //Utils.Debug.print("Ordner: " + TheChildFolder.Name);
                                    foreach (object oItem in childFolder.Items)
                                    {
                                        if (oItem is Outlook.MailItem)
                                        {
                                            string subject = "not assigned";
                                            try
                                            {

                                                Outlook.MailItem item = oItem as Outlook.MailItem;
                                                subject = item.Subject;
                                                folder.AddMailContent(item.Body, item.Subject, item.SenderEmailAddress, item.To);
                                            }
                                            catch (COMException ex)
                                            {
                                                //log file entry
                                                String message = "It isn't possible to index mail, mail subject:\n"
                                                                + subject + "in folder " + folderPath;
                                                Utils.Debug.Log(message, ex, TraceEventType.Critical);
                                            }
                                        }
                                    }
                                    folderList.Add(folder);
                                    folderIndex++;
                                }
                                CreateMailData(childFolder, folderList);
                            }
                            if (CheckIsInbox(childFolder))
                            {
                                CreateMailData(childFolder, folderList);
                            }
                        }
                        catch (COMException ex)
                        {
                            //log file entry
                            String message = "It isn't possible to index folder, folderpath: " + folderPath;
                            Utils.Debug.Log(message, ex, TraceEventType.Critical);
                        }
                    }
                }
            }
            catch (COMException ex)
            {
                //log file entry
                String message = "It isn't possible to index the root folder.";
                Utils.Debug.Log(message, ex, TraceEventType.Critical);
            }
        }

        /// <summary>
        /// This method checks if the folder is type of Inbox Folder.
        /// </summary>
        /// <param name="folder">folder to check</param>
        /// <returns>true if is inbox, else false</returns>
        private bool CheckIsInbox(Outlook.Folder folder)
        {
            if (folder.Name.ToLower() ==
                nameSpace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Name.ToLower()
                || folder.Name.ToLower() == "inbox")
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// This method checks the folder default item type and if the folder is type of default folders.
        /// </summary>
        /// <param name="folder">in folder to check</param>
        /// <returns>
        /// true, if folder could be used
        /// false, if folder schould be banned
        /// </returns>
        private bool ChekFolder(Outlook.Folder folder)
        {
            if (folder.DefaultItemType == Outlook.OlItemType.olMailItem)
            {
                foreach (String DefaultFolderName in defaultFolderList)
                {
                    if (folder.Name.ToLower() == DefaultFolderName.ToLower())
                    {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// This method creates a list of string items, which represent the default folders in
        /// outlook to exclude them in the calculation of the classification.
        /// </summary>
        private void CreateDefaultFolderList()
        {
            defaultFolderList = new List<String>();

            foreach (Outlook.OlDefaultFolders folderType in
                     Outlook.OlDefaultFolders.GetValues(typeof(Outlook.OlDefaultFolders)))
            {
                AddToDefaultFolderList(folderType);
            }
            defaultFolderList.Add("deleted items");
            defaultFolderList.Add("sent items");
        }

        /// <summary>
        /// This method adds the specified default folder to the list of folders.
        /// </summary>
        private void AddToDefaultFolderList(Outlook.OlDefaultFolders folderType)
        {
            Outlook.MAPIFolder folder;
            try
            {
                folder = nameSpace.GetDefaultFolder(folderType);
                defaultFolderList.Add(folder.Name);
            }
            catch (COMException ex)
            {
                // this folderType does not exist; do nothing
                String message = "The Default foldertype does not exist!";
                Utils.Debug.Log(message, ex, TraceEventType.Information);
            }
        }

        /// <summary>
        /// This Method make a refresh of the dataset.
        /// </summary>
        public void refresh()
        {
            isDataSetLoaded = false;
            loadCustomProperties();
            Thread thread = new Thread(new ThreadStart(RefreshDataSet));
            thread.Start();
        }

        /// <summary>
        /// This Method sets the custom properties to application.
        /// </summary>
        public void loadCustomProperties()
        {
            MailContent.SetUseStopwords(Settings.Default.luceneAnalyzer);
            classificationHandler.SetClassifier(Settings.Default.classifierIndex);
            this.SetTrackbarIndex();
        }

        public void SetTrackbarIndex()
        {
            classificationHandler.SetCountOfShownFolderInContextMenu(Settings.Default.trackBarIndex);
        }

        /// <summary>
        /// This Method loads the dataset of outlook.
        /// </summary>
        public void Load()
        {
            isDataSetLoaded = false;
            Thread thread = new Thread(new ThreadStart(LoadDataSet));
            thread.Start();
        }

        /// <summary>
        /// This Mehtod loads the mail dataset.
        /// </summary>
        private void LoadDataSet()
        {
            //Clear all
            folderList.Clear();
            classificationHandler.Clear();

            //load Mail Data
            folderList = LoadMailData();
            classificationHandler.CreateInstances(folderList);

            //Train Classifier
            classificationHandler.TrainClassifier();

            //Set up
            isDataSetLoaded = true;
        }

        /// <summary>
        /// This Mehtod refresh the dataSet.
        /// </summary>
        private void RefreshDataSet()
        {
            //Clear all
            classificationHandler.Clear();

            //load Mail Data
            classificationHandler.CreateInstances(folderList);

            //Train Classifier
            classificationHandler.TrainClassifier();

            //Set up
            isDataSetLoaded = true;
        }

        /// <summary>
        /// This method saves dataset to ARFF file.
        /// </summary>
        /// <param name="filePath">the destination path inkl file name</param>
        /// <param name="isFiltered">
        /// true, the arff file is filtered with the StringToWord filter
        /// false, the arff file isn't filtered, it's possible to read the content of it
        /// </param>
        public void SaveToFile(string filePath, bool isFiltered)
        {
            classificationHandler.WriteInstance(filePath, isFiltered);
        }

        /// <summary>
        /// Is the data set loaded?
        /// </summary>
        /// <returns>true if the dataset has been loaded</returns>
        public bool IsDataSetLoaded()
        {
            return isDataSetLoaded;
        }

        /// <summary>
        /// This method returns the Goodness of the selected classifier.
        /// </summary>
        /// <returns>goodness in percent</returns>
        public int getGoodness()
        {
            return classificationHandler.GetGoodness();
        }

        /// <summary>
        /// This method returns the whole classifier test results.
        /// </summary>
        /// <returns>the whole testresults as a string.</returns>
        public string getGoodnessExtra()
        {
            return classificationHandler.GetGoodnessExtra();
        }
    }
}